import requests
import time
import hashlib
import hmac
import json
from decimal import Decimal


class BinanceLimitOrder:
    def __init__(self, api_key: str, api_secret: str, testnet: bool = True):
        self.api_key = api_key
        self.api_secret = api_secret
        self.base_url = "https://testnet.binance.vision" if testnet else "https://api.binance.com"
        self.time_offset = 0
        self._sync_time()

    def _sync_time(self):
        """同步服务器时间"""
        try:
            response = requests.get(f"{self.base_url}/api/v3/time", timeout=5)
            if response.status_code == 200:
                server_time = response.json()['serverTime']
                local_time = int(time.time() * 1000)
                self.time_offset = server_time - local_time
                print(f"时间同步成功，偏移: {self.time_offset}ms")
        except Exception as e:
            print(f"时间同步失败: {e}")

    def _get_timestamp(self) -> int:
        """获取同步后的时间戳"""
        return int(time.time() * 1000) + self.time_offset

    def _create_signature(self, query_string: str) -> str:
        """创建HMAC签名"""
        return hmac.new(
            self.api_secret.encode('utf-8'),
            query_string.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()

    def get_symbol_info(self, symbol: str = "BNBUSDT") -> dict:
        """获取交易对信息"""
        try:
            response = requests.get(f"{self.base_url}/api/v3/exchangeInfo?symbol={symbol}")
            data = response.json()
            if 'symbols' in data:
                for s in data['symbols']:
                    if s['symbol'] == symbol:
                        return s
            return {}
        except Exception as e:
            print(f"获取交易对信息失败: {e}")
            return {}

    def get_current_price(self, symbol: str = "BNBUSDT") -> Decimal:
        """获取当前价格"""
        try:
            response = requests.get(f"{self.base_url}/api/v3/ticker/price?symbol={symbol}")
            data = response.json()
            return Decimal(data['price'])
        except Exception as e:
            print(f"获取当前价格失败: {e}")
            return Decimal('0')

    def validate_order_params(self, symbol: str, quantity: Decimal, price: Decimal) -> dict:
        """验证订单参数是否符合交易对规则"""
        symbol_info = self.get_symbol_info(symbol)
        if not symbol_info:
            return {"valid": False, "error": "无法获取交易对信息"}

        # 检查交易对是否可交易
        if symbol_info.get('status') != 'TRADING':
            return {"valid": False, "error": f"交易对{symbol}不可交易"}

        # 获取数量过滤器
        lot_size_filter = next(
            (f for f in symbol_info['filters'] if f['filterType'] == 'LOT_SIZE'),
            None
        )

        # 获取价格过滤器
        price_filter = next(
            (f for f in symbol_info['filters'] if f['filterType'] == 'PRICE_FILTER'),
            None
        )

        # 获取最小交易金额过滤器
        min_notional_filter = next(
            (f for f in symbol_info['filters'] if f['filterType'] == 'MIN_NOTIONAL'),
            None
        )

        errors = []

        # 验证数量
        if lot_size_filter:
            min_qty = Decimal(lot_size_filter['minQty'])
            max_qty = Decimal(lot_size_filter['maxQty'])
            step_size = Decimal(lot_size_filter['stepSize'])

            if quantity < min_qty:
                errors.append(f"数量{quantity}小于最小值{min_qty}")
            if quantity > max_qty:
                errors.append(f"数量{quantity}大于最大值{max_qty}")

            # 检查数量是否符合步长要求
            remainder = quantity % step_size
            if remainder != Decimal('0'):
                errors.append(f"数量{quantity}不符合步长要求{step_size}")

        # 验证价格
        if price_filter:
            min_price = Decimal(price_filter['minPrice'])
            max_price = Decimal(price_filter['maxPrice'])
            tick_size = Decimal(price_filter['tickSize'])

            if price < min_price:
                errors.append(f"价格{price}小于最小值{min_price}")
            if price > max_price:
                errors.append(f"价格{price}大于最大值{max_price}")

            # 检查价格是否符合tick size要求
            remainder = price % tick_size
            if remainder != Decimal('0'):
                errors.append(f"价格{price}不符合tick size要求{tick_size}")

        # 验证最小交易金额
        if min_notional_filter:
            min_notional = Decimal(min_notional_filter['minNotional'])
            notional_value = quantity * price

            if notional_value < min_notional:
                errors.append(f"交易金额{notional_value}小于最小值{min_notional}")

        if errors:
            return {"valid": False, "errors": errors}
        else:
            return {"valid": True}

    def place_limit_order(self, symbol: str, side: str, quantity: Decimal, price: Decimal) -> dict:
        """
        下限价委托单

        Args:
            symbol: 交易对，如 "BNBUSDT"
            side: 方向，"BUY" 或 "SELL"
            quantity: 数量
            price: 价格

        Returns:
            订单结果
        """
        # 验证订单参数
        validation = self.validate_order_params(symbol, quantity, price)
        if not validation["valid"]:
            return {
                "success": False,
                "error": "参数验证失败",
                "details": validation.get("errors", validation.get("error", "未知错误"))
            }

        # 获取当前价格
        current_price = self.get_current_price(symbol)
        print(f"当前市场价格: {current_price}")
        print(f"委托价格: {price}")
        print(f"价差: {abs(current_price - price)} ({abs((current_price - price) / current_price * 100):.2f}%)")

        # 确认交易
        confirm = input(f"确认以{price}价格{side} {quantity} {symbol}? (y/n): ")
        if confirm.lower() != 'y':
            return {"success": False, "error": "用户取消"}

        # 准备订单参数
        endpoint = "/api/v3/order"
        timestamp = self._get_timestamp()

        # 格式化数量和价格到适当精度
        symbol_info = self.get_symbol_info(symbol)
        quantity_precision = next(
            (f for f in symbol_info['filters'] if f['filterType'] == 'LOT_SIZE'),
            {}
        ).get('stepSize', '0.00000001')

        price_precision = next(
            (f for f in symbol_info['filters'] if f['filterType'] == 'PRICE_FILTER'),
            {}
        ).get('tickSize', '0.00000001')

        # 格式化数量
        qty_precision = len(quantity_precision.split('.')[1]) if '.' in quantity_precision else 8
        formatted_quantity = round(quantity, qty_precision)

        # 格式化价格
        price_precision = len(price_precision.split('.')[1]) if '.' in price_precision else 8
        formatted_price = round(price, price_precision)

        # 构建查询字符串
        params = (
            f"symbol={symbol}&"
            f"side={side}&"
            f"type=LIMIT&"
            f"timeInForce=GTC&"  # 成交为止，订单会一直有效，直到被成交或取消
            f"quantity={formatted_quantity}&"
            f"price={formatted_price}&"
            f"timestamp={timestamp}&"
            f"recvWindow=5000"
        )

        # 创建签名
        signature = self._create_signature(params)
        url = f"{self.base_url}{endpoint}?{params}&signature={signature}"

        # 设置请求头
        headers = {"X-MBX-APIKEY": self.api_key}

        try:
            # 发送POST请求
            response = requests.post(url, headers=headers, timeout=10)
            result = response.json()

            if "orderId" in result:
                print("委托单提交成功!")
                return {"success": True, "data": result}
            else:
                print("委托单提交失败!")
                return {"success": False, "error": result}

        except Exception as e:
            error_msg = f"请求失败: {str(e)}"
            print(error_msg)
            return {"success": False, "error": error_msg}

    def get_order_status(self, symbol: str, order_id: int) -> dict:
        """查询订单状态"""
        endpoint = "/api/v3/order"
        timestamp = self._get_timestamp()

        params = f"symbol={symbol}&orderId={order_id}&timestamp={timestamp}&recvWindow=5000"
        signature = self._create_signature(params)

        url = f"{self.base_url}{endpoint}?{params}&signature={signature}"
        headers = {"X-MBX-APIKEY": self.api_key}

        try:
            response = requests.get(url, headers=headers, timeout=10)
            return response.json()
        except Exception as e:
            return {"error": str(e)}


# 使用示例
def main():
    # 从环境变量获取API密钥
    import os
    API_KEY = os.getenv('BINANCE_API_KEY', 'your_api_key_here')
    API_SECRET = os.getenv('BINANCE_API_SECRET', 'your_api_secret_here')

    # 创建交易客户端
    trader = BinanceLimitOrder(API_KEY, API_SECRET, testnet=True)

    # 设置订单参数
    symbol = "BNBUSDT"
    side = "BUY"
    quantity = Decimal('0.01')  # 0.01 BNB
    price = Decimal('969.38')  # 价格969.38 USDT

    print(f"准备下限量价单: {side} {quantity} {symbol} @ {price}")

    # 下限价单
    result = trader.place_limit_order(symbol, side, quantity, price)

    if result["success"]:
        order_data = result["data"]
        print("\n委托单详情:")
        print(f"订单ID: {order_data['orderId']}")
        print(f"客户端订单ID: {order_data.get('clientOrderId', 'N/A')}")
        print(f"交易对: {order_data['symbol']}")
        print(f"方向: {order_data['side']}")
        print(f"类型: {order_data['type']}")
        print(f"数量: {order_data['origQty']}")
        print(f"价格: {order_data['price']}")
        print(f"状态: {order_data['status']}")
        print(f"时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(order_data['transactTime'] / 1000))}")

        # 等待一段时间后查询订单状态
        print("\n等待5秒后查询订单状态...")
        time.sleep(5)

        order_status = trader.get_order_status(symbol, order_data['orderId'])
        if 'status' in order_status:
            print(f"当前订单状态: {order_status['status']}")
            print(f"已成交数量: {order_status['executedQty']}")
    else:
        print(f"下单失败: {result['error']}")


if __name__ == "__main__":
    main()